# DWH_CS_086_test.py
import pytest
import logging

from selenium.webdriver.common.by import By

from tests.utils import htmlUtils
from time import sleep
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement

'''
测试用例编号: DWH_CS_086
测试用例名称: 数据可视化功能测试用例
步骤1: 点击数据可视化菜单
步骤2: 点击绘制图表按钮，选择数据源supplier，选择图表类型表，点击创建新图表
步骤3: 填写图表名称test_user_01，查询模式选择聚合，选择维度s_name，选择指标COUNT(*)，点击保存并创建图表
预期结果: 图表创建成功，页面出现创建成功的提示
'''


class Test_DWH_CS_086:
    def setup_class(self):
        logging.info("开始执行测试用例 DWH_CS_086")

    def test_case_086(self, driver: WebDriver):
        self.step1_navigate_to_visualization(driver)
        self.step2_create_new_chart(driver)
        self.step3_configure_chart(driver)
        sleep(2)
        assert self.check_chart_created(driver)

    def teardown_class(self):
        logging.info("结束执行测试用例 DWH_CS_086")

    def step1_navigate_to_visualization(self, driver: WebDriver):
        """导航到数据可视化页面"""
        elements: list[WebElement] = htmlUtils.getElementsByTagName(driver, 'li')
        for element in elements:
            logging.info(f"菜单项文本：{element.accessible_name}")
            if element.accessible_name == "数据可视化":
                element.click()
                break

        sleep(3)
        # 修改验证条件，适配实际的Superset集成页面
        assert "superset" in driver.current_url, "未跳转到Superset页面"

    def step2_create_new_chart(self, driver: WebDriver):
        """创建新图表"""
        # 等待页面加载完成
        sleep(3)

        # 查找并点击"绘制图表"导航项（在主页面中）
        try:
            nav_items = htmlUtils.getElementsByTagName(driver, 'li')
            chart_button_found = False
            for item in nav_items:
                try:
                    item_text = item.text
                    # 在主页面中查找"绘制图表"按钮
                    if "绘制图表" in item_text:
                        item.click()
                        logging.info(f"成功点击绘制图表导航项: {item_text}")
                        chart_button_found = True
                        break
                except:
                    continue

            if not chart_button_found:
                raise Exception("未找到绘制图表导航项")
        except Exception as e:
            logging.warning(f"点击绘制图表导航项失败: {e}")
            raise

        # 等待iframe内容更新
        sleep(5)

        # 切换到iframe内部操作
        try:
            iframe = htmlUtils.getElementByXPath(driver, '//iframe')
            driver.switch_to.frame(iframe)
            logging.info("成功切换到Superset iframe")
        except Exception as e:
            logging.error(f"切换到iframe失败: {e}")
            raise

        # 在iframe内点击新建图表按钮
        try:
            # 精确定位策略：根据提供的HTML结构
            chart_buttons = htmlUtils.getElementsByXPath(driver,
                                                         '//button[contains(@class, "antd5-btn-primary") and .//span[@class="anticon-plus"] and .//span[text()="图表"]]')

            # 备选策略1：稍微宽松的匹配
            if not chart_buttons:
                chart_buttons = htmlUtils.getElementsByXPath(driver,
                                                             '//button[contains(@class, "primary") and .//span[text()="图表"]]')

            if chart_buttons:
                # 尝试点击
                try:
                    chart_buttons[0].click()
                    logging.info("成功点击新建图表按钮")
                except:
                    # 使用JavaScript点击
                    driver.execute_script("arguments[0].click();", chart_buttons[0])
                    logging.info("通过JavaScript点击新建图表按钮")
            else:
                raise Exception("未找到新建图表按钮")
        except Exception as e:
            logging.error(f"点击新建图表按钮失败: {e}")
            raise

        sleep(3)  # 等待数据源选择页面加载

        # 选择数据源 supplier
        try:
            # 查找数据源选择框
            datasource_select = htmlUtils.getElementByXPath(driver, '//input[@aria-label="数据集"]')
            datasource_select.click()
            sleep(2)  # 增加等待时间，确保下拉列表加载完成

            # 查找并选择 supplier 选项 - 使用更准确的定位方式
            # 根据您提供的HTML，supplier在span标签中
            supplier_options = htmlUtils.getElementsByXPath(driver,
                                                            '//span[contains(@class, "css-l4488p") and text()="supplier"]')
            if not supplier_options:
                # 备选方案：查找包含supplier文本的div或其他元素
                supplier_options = htmlUtils.getElementsByXPath(driver, '//div[contains(text(), "supplier")]')

            if supplier_options:
                supplier_options[0].click()
                logging.info("成功选择数据源 supplier")
            else:
                # 如果还找不到，输出页面上所有相关元素用于调试
                try:
                    all_options = htmlUtils.getElementsByTagName(driver, 'span')
                    logging.info("页面上所有span元素:")
                    for i, opt in enumerate(all_options[:20]):  # 显示前20个
                        try:
                            opt_text = opt.text
                            opt_class = opt.get_attribute('class')
                            logging.info(f"  span{i}: text='{opt_text}', class='{opt_class}'")
                        except:
                            pass
                except:
                    pass
                raise Exception("未找到 supplier 数据源选项")
        except Exception as e:
            logging.error(f"选择数据源时出错: {e}")
            raise

        sleep(2)

        # 选择图表类型为表
        try:
            # 查找图表类型选择区域，选择"表"类型
            table_types = htmlUtils.getElementsByXPath(driver,
                                                       '//div[contains(@class, "viztype-label") and text()="表"]')
            if table_types:
                # 点击包含"表"标签的父元素
                table_types[0].click()
                logging.info("成功选择图表类型: 表")
            else:
                raise Exception("未找到图表类型选项: 表")
        except Exception as e:
            logging.error(f"选择图表类型时出错: {e}")
            raise

        sleep(2)

        # 点击创建新图表按钮
        try:
            # 使用更精确的XPath定位策略
            create_buttons = htmlUtils.getElementsByXPath(driver,
                                                          '//button[contains(@class, "antd5-btn-primary") and .//span[text()="创建新图表"]]')

            # 备选策略1：查找包含"创建新图表"文本的按钮
            if not create_buttons:
                create_buttons = htmlUtils.getElementsByXPath(driver,
                                                              '//button[.//span[contains(text(), "创建新图表")]]')

            # 备选策略2：查找所有primary按钮并筛选
            if not create_buttons:
                primary_buttons = htmlUtils.getElementsByXPath(driver,
                                                               '//button[contains(@class, "primary")]')
                for btn in primary_buttons:
                    try:
                        if "创建新图表" in btn.text:
                            create_buttons = [btn]
                            break
                    except:
                        continue

            # 备选策略3：直接查找包含"创建新图表"文本的按钮
            if not create_buttons:
                create_buttons = htmlUtils.getElementsByXPath(driver,
                                                              '//button[contains(text(), "创建新图表")]')

            if create_buttons:
                create_buttons[0].click()
                logging.info("成功点击创建新图表按钮")
            else:
                # 输出页面上所有按钮信息用于调试
                try:
                    all_buttons = htmlUtils.getElementsByTagName(driver, 'button')
                    logging.info("页面上所有按钮:")
                    for i, btn in enumerate(all_buttons[:15]):  # 显示前15个
                        try:
                            btn_text = btn.text
                            btn_class = btn.get_attribute('class')
                            logging.info(f"  按钮{i}: text='{btn_text}', class='{btn_class}'")
                        except:
                            pass
                except:
                    pass
                raise Exception("未找到创建新图表按钮")
        except Exception as e:
            logging.error(f"点击创建新图表按钮失败: {e}")
            raise

        # 切回主文档
        try:
            driver.switch_to.default_content()
            logging.info("切回主文档")
        except:
            pass

        sleep(5)  # 等待图表配置页面加载

    def step3_configure_chart(self, driver: WebDriver):
        """配置图表参数"""
        # 等待配置页面加载
        sleep(2)

        # 切换到iframe内部操作
        try:
            iframe = htmlUtils.getElementByXPath(driver, '//iframe')
            driver.switch_to.frame(iframe)
            logging.info("成功切换到Superset iframe进行图表配置")
        except Exception as e:
            logging.error(f"切换到iframe进行图表配置失败: {e}")
            raise

        try:
            # 建议使用更自然的输入方式
            try:
                # 查找图表名称输入框
                name_inputs = htmlUtils.getElementsByXPath(driver, '//input[@aria-label="Chart title"]')
                if not name_inputs:
                    name_inputs = htmlUtils.getElementsByXPath(driver,
                                                               '//input[contains(@placeholder, "Add the name of the chart")]')
                if not name_inputs:
                    name_inputs = htmlUtils.getElementsByCssSelector(driver, 'input.dynamic-title-input')

                if name_inputs:
                    name_input = name_inputs[0]
                    # 先点击获得焦点
                    name_input.click()
                    sleep(0.5)

                    # 清除并输入内容
                    name_input.clear()
                    name_input.send_keys("test_user_01")

                    # 触发失去焦点事件
                    from selenium.webdriver.common.keys import Keys
                    name_input.send_keys(Keys.TAB)

                    logging.info("成功填写图表名称")
                    sleep(1)  # 等待界面更新
                else:
                    raise Exception("未找到图表名称输入框")
            except Exception as e:
                logging.warning(f"填写图表名称时出错: {e}")
                raise
            sleep(3)

            # 在每次操作后增加等待时间并重新定位元素
            # 2. 将左边指标栏里的COUNT(*)指标拖入右侧指标中
            try:
                # 查找左侧指标列表中的COUNT(*)指标
                left_count_metrics = htmlUtils.getElementsByXPath(driver,
                                                                  '//div[@draggable="true"]//span[contains(text(), "COUNT(*)")]')

                # 查找右侧指标放置区域 - 使用您提供的精确定位
                right_metric_dropzones = htmlUtils.getElementsByXPath(driver,
                                                                      '//div[contains(@class, "css-rnxy7r") and contains(text(), "将列/指标拖放到此处")]')

                if left_count_metrics and right_metric_dropzones:
                    # 获取可拖拽的源元素
                    source_element = left_count_metrics[0].find_element(By.XPATH, "./ancestor::div[@draggable='true']")
                    target_element = right_metric_dropzones[0]

                    # 滚动到元素可见位置
                    driver.execute_script("arguments[0].scrollIntoView(true);", source_element)
                    driver.execute_script("arguments[0].scrollIntoView(true);", target_element)
                    sleep(1)

                    # 使用ActionChains进行拖拽操作
                    from selenium.webdriver.common.action_chains import ActionChains
                    actions = ActionChains(driver)
                    actions.drag_and_drop(source_element, target_element).perform()

                    logging.info("成功将COUNT(*)指标拖入右侧指标区域")
                    sleep(3)  # 增加等待时间让界面稳定

                    # 拖拽后重新检查界面状态
                    # 检查是否出现了新的界面元素
                    new_elements = htmlUtils.getElementsByXPath(driver, '//label[contains(text(), "查询模式")]')
                    if new_elements:
                        logging.info("拖拽后界面已切换到配置模式")
                else:
                    logging.info(f"找到左侧COUNT(*)元素数量: {len(left_count_metrics) if left_count_metrics else 0}")
                    logging.info(
                        f"找到右侧指标区域数量: {len(right_metric_dropzones) if right_metric_dropzones else 0}")
                    raise Exception("未找到指标元素或放置区域")
            except Exception as e:
                logging.warning(f"拖拽指标时出错: {e}")
                raise

            sleep(2)
            # 3. 将左边列栏里的s_name拖入右侧Dimension中
            try:
                # 查找左侧列列表中的s_name维度
                left_s_name_columns = htmlUtils.getElementsByXPath(driver,
                                                                   '//div[@draggable="true"]//span[contains(text(), "s_name")]')

                # 查找右侧维度放置区域 - 使用与指标类似的精确定位方式
                right_dimension_dropzones = htmlUtils.getElementsByXPath(driver,
                                                                         '//div[contains(@class, "css-rnxy7r") and contains(text(), "Drop columns here")]')

                # 备选方案：通过Dimensions标签定位
                if not right_dimension_dropzones:
                    right_dimension_dropzones = htmlUtils.getElementsByXPath(driver,
                                                                             '//label[contains(text(), "Dimensions")]/following::div[contains(@class, "css-rnxy7r")]')

                if left_s_name_columns and right_dimension_dropzones:
                    # 获取可拖拽的源元素
                    source_element = left_s_name_columns[0].find_element(By.XPATH, "./ancestor::div[@draggable='true']")
                    target_element = right_dimension_dropzones[0]

                    # 滚动到元素可见位置
                    driver.execute_script("arguments[0].scrollIntoView(true);", source_element)
                    driver.execute_script("arguments[0].scrollIntoView(true);", target_element)
                    sleep(1)

                    # 使用ActionChains进行拖拽操作
                    from selenium.webdriver.common.action_chains import ActionChains
                    actions = ActionChains(driver)
                    actions.drag_and_drop(source_element, target_element).perform()

                    logging.info("成功将s_name维度拖入右侧维度区域")
                    sleep(2)  # 等待界面更新
                else:
                    logging.info(f"找到左侧s_name元素数量: {len(left_s_name_columns) if left_s_name_columns else 0}")
                    logging.info(
                        f"找到右侧维度区域数量: {len(right_dimension_dropzones) if right_dimension_dropzones else 0}")
                    raise Exception("未找到维度元素或放置区域")
            except Exception as e:
                logging.warning(f"拖拽维度时出错: {e}")
                raise

            sleep(2)

            # 4. 点击create chart按钮
            try:
                # 使用更精确的XPath定位策略
                create_buttons = htmlUtils.getElementsByXPath(driver,
                                                              '//button[contains(@class, "antd5-btn-primary") and .//span[text()="Create chart"]]')

                # 备选策略1：查找包含Create chart文本的按钮
                if not create_buttons:
                    create_buttons = htmlUtils.getElementsByXPath(driver,
                                                                  '//button[.//span[contains(text(), "Create chart")]]')

                # 备选策略2：查找具有primary类且文本包含Create的按钮
                if not create_buttons:
                    create_buttons = htmlUtils.getElementsByXPath(driver,
                                                                  '//button[contains(@class, "primary") and contains(text(), "Create")]')

                # 备选策略3：直接查找包含Create chart文本的按钮
                if not create_buttons:
                    create_buttons = htmlUtils.getElementsByXPath(driver,
                                                                  '//button[contains(text(), "Create chart")]')

                if create_buttons:
                    create_buttons[0].click()
                    logging.info("成功点击Create chart按钮")
                else:
                    raise Exception("未找到Create chart按钮")
            except Exception as e:
                logging.warning(f"点击Create chart按钮时出错: {e}")
                raise

            sleep(5)  # 等待图表创建完成

            # 5. 点击保存按钮
            try:
                # 等待界面稳定
                sleep(3)

                # 查找主界面的保存按钮
                save_buttons = htmlUtils.getElementsByXPath(driver,
                                                            '//button[contains(@class, "superset-button-secondary") and .//span[text()="保存"]]')

                # 如果找不到，尝试查找模态框中的保存按钮
                if not save_buttons:
                    save_buttons = htmlUtils.getElementsByXPath(driver,
                                                                '//button[@id="btn_modal_save" and contains(text(), "保 存")]')

                if save_buttons:
                    # 使用JavaScript点击避免被遮挡
                    driver.execute_script("arguments[0].click();", save_buttons[0])
                    logging.info("成功点击保存按钮")

                    # 等待弹窗出现
                    sleep(3)

                    # 查找看板选择下拉框
                    dashboard_select = htmlUtils.getElementByXPath(driver, '//input[@aria-label="选择看板"]')
                    if dashboard_select:
                        # 点击下拉框
                        dashboard_select.click()
                        sleep(1)

                        # 查找并选择Test086看板
                        test086_options = htmlUtils.getElementsByXPath(driver, '//div[@label="Test086"]')
                        if test086_options:
                            test086_options[0].click()
                            logging.info("成功选择Test086看板")
                            sleep(1)

                            # 查找并点击模态框中的保存按钮 - 改进的定位策略
                            modal_save_buttons = htmlUtils.getElementsByXPath(driver, '//button[@id="btn_modal_save"]')
                            if not modal_save_buttons:
                                modal_save_buttons = htmlUtils.getElementsByXPath(driver,
                                                                                  '//button[contains(@class, "superset-button-primary") and contains(text(), "保 存")]')
                            if not modal_save_buttons:
                                modal_save_buttons = htmlUtils.getElementsByTagName(driver, 'button')
                                # 筛选包含"保 存"文本的按钮
                                for btn in modal_save_buttons:
                                    try:
                                        if "保 存" in btn.text and "primary" in (btn.get_attribute('class') or ''):
                                            modal_save_buttons = [btn]
                                            break
                                    except:
                                        continue

                            if modal_save_buttons:
                                modal_save_buttons[0].click()
                                logging.info("成功点击模态框保存按钮")
                            else:
                                raise Exception("未找到模态框保存按钮")
                        else:
                            raise Exception("未找到Test086看板选项")
                    else:
                        logging.info("未找到看板选择下拉框，可能保存已完成")

                    # 等待保存完成
                    sleep(2)
                else:
                    raise Exception("未找到保存按钮")
            except Exception as e:
                logging.warning(f"点击保存按钮时出错: {e}")
                raise

        finally:
                pass

        sleep(3)

    def check_chart_created(self, driver: WebDriver) -> bool:
        """验证图表是否创建成功"""
        try:
            # 检查是否在图表展示页面
            if "chart" in driver.current_url and "new" not in driver.current_url:
                logging.info("图表创建成功，已跳转到图表展示页面")
                return True

            # 查找包含test_user_01的元素来验证图表创建成功
            chart_elements = htmlUtils.getElementsByXPath(driver, '//h1[contains(text(), "test_user_01")]')
            if len(chart_elements) > 0:
                logging.info("图表创建成功，找到图表标题")
                return True

            # 检查是否有图表渲染区域
            chart_containers = htmlUtils.getElementsByXPath(driver, '//div[contains(@class, "chart-container")]')
            if len(chart_containers) > 0:
                logging.info("图表创建成功，检测到图表容器")
                return True

            return False
        except Exception as e:
            logging.error(f"验证图表创建失败: {e}")
            return False
